#!usr/bin/env python3
#-*- coding:utf-8 -*-

'''
由于Web框架使用了基于asyncio的aiohttp，这是基于协程的异步模型。在协程中，不能调用普通的同步IO操作，
因为所有用户都是由一个线程服务的，协程的执行速度必须非常快，才能处理大量用户的请求。
而耗时的IO操作不能在协程中以同步的方式调用，否则，等待一个IO操作时，系统无法响应任何其他用户。
这就是异步编程的一个原则：一旦决定使用异步，则系统每一层都必须是异步，“开弓没有回头箭”。
幸运的是aiomysql为MySQL数据库提供了异步IO的驱动。
'''
__author__ = 'zhou'

#通过大量协程完成的orm框架

import asyncio,logging
import aiomysql

def log(sql, args=()):
    logging.info('SQL: %s' % sql)

@asyncio.coroutine
def create_pool(loop,**kw):
	logging.info('create database connection pool...')
	global __pool #全局变量保存数据库的连接池
	__pool = yield from aiomysql.create_pool(
			host        = kw.get('host','127.0.0.1'), #默认ip
			port        = kw.get('port',3306), #端口
			user        = kw['user'], #端口
			password    = kw['password'], #密码
			db          = kw['db'], #数据库名
			charset     = kw['charset'], #数据库字符集
			autocommit  = kw['autocommit'], #是否自动提交事务
			maxsize     = kw['maxsize'], #是否自动提交事务
			min         = kw['minsize'], #是否自动提交事务
			loop        = loop
		)

@asyncio.coroutine
def select(sql,args,size=None):
	log(sql,args)
	global __pool
	with(yield from __pool) as conn:
		cur = yield from conn.cursor(aiomysql.DictCursor)
	#注意要始终坚持使用带参数的SQL，而不是自己拼接SQL字符串，这样可以防止SQL注入攻击。
	yield from cur.execute(sql.replace('?','%s'),args or ()) #SQL语句的占位符是?，而MySQL的占位符是%s，select()函数在内部自动替换
	if size:
		rs = yield from cur.fetchmany(size)
	else:
		rs = yield from fetchall()
	yield from cur.close()
	logging.info('row returned:%s'%len(rs))
	return rs

		
#要执行INSERT、UPDATE、DELETE语句，可以定义一个通用的execute()函数
@asyncio.coroutine
def execute(sql,args):
	log(sql,args)
	with (yield from __pool) as conn:
		try:
			cur = yield from conn.cursor() #当前数据库游标
			yield from cur.execute(sql.replace('?','%s'),args)
			affected = cur.rowcount
			yield from cur.close()
		except BaseException as e:
			raise 
		return affected
			
	
class ModelMetaClass(type):
	def __new__(cls,name,base,attrs):
		if name == "Model":
			return type.__new__(cls,name,base,attrs);
		tablename = attrs.get("__table__",None) or name;
		logging.info('found model : %s(table:%s)'%(name,table))
		#获取所有字段和主键名
		mappings = dict();
		fields = [];
		primaryKey = None;
		for k,v in attrs.items():
			if isinstance(v,Field):
				logging.info("find mappings:%s==>%s"%(k,v))
				mappings[k] = v;
				if v.primary_key:
					if primaryKey: 
						raise RuntimeError('Duplicate primary key for field: %s' % k)
					primaryKey = k;
				else:
					fields.append(k)
			if not primaryKey:
				raise RuntimeError('Primary key not found')
			for k in mappings.keys():
				attrs.pop(k)
		escaped_fields = list(map(lambda f:'`%s`' % f,fields))
		arrts['__mappings'] = mappings;# 保存属性和列的映射关系
		arrts['__table__'] = tablename;
		attrs['__prmary_key__'] = primaryKey; #主键属性名
		attrs['__fields__'] = fields; #除了主键之外的属性名
		attrs['__select__'] = 'select `%s`,`%s`' % (primaryKey,','.join(fields))		

		return type.__new__(cls,name,base,attrs)

class Model(dict,metaClass=ModelMetaClass):
	def __init__(self,**kw):
		super(Model,self).__init__(kw) #执行父类的构造方法
	def __getattr__(self,key):
		try:
			return slef[key]
		except KeyError:
			raise AttributeError(r"'Model' object has no attribute '%s'" % key)
			
	def __setattr__(self,key,value):
		self[key] = value
	def getValue():
		return getattr(self,key,None) #如果key值不存在返回None
		
	def getValueOrDefault(self,key):
		value = self.getValue(key)
		if value is None:
			filed = self.__mapings__[key]
			if filed.default is not None:
				#filed.default如果是一个可调用的函数则value等于函数执行的结构否则value等于filed.default
				value = filed.default() if callable(filed.default) else filed.default
				logging.debug('using default vaule for %s:%s' %s (key,str(value)))
				setattr(self,key,value)
		return value
	

class Field(object):
	pass

class StringField(Field):
	pass

